package com.nulldev.util.graphics.renderIt.ginterfaces.nitrous;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.io.File;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

import javax.swing.JFrame;
import javax.swing.JPanel;

import com.nulldev.util.VariableAPI.ArrayUtils;
import com.nulldev.util.VariableAPI.MathUtil;
import com.nulldev.util.VariableAPI.StringsUtil;
import com.nulldev.util.data.variable;
import com.nulldev.util.graphics.renderIt.api.renderItGraphicsInterface;
import com.nulldev.util.graphics.renderIt.api.core.IRenderCanvas;
import com.nulldev.util.graphics.renderIt.api.core.RITFont;
import com.nulldev.util.graphics.renderIt.api.envcaps.renderItCanvasOperations;
import com.nulldev.util.graphics.renderIt.api.envcaps.renderItOperations;
import com.nulldev.util.graphics.renderIt.api.math.Vector2f;
import com.nulldev.util.graphics.renderIt.api.math.Vector4f;
import com.nulldev.util.graphics.renderIt.api.utils.RITVSyncTimer;
import com.nulldev.util.graphics.renderIt.api.utils.RITViewportUtils;
import com.nulldev.util.graphics.renderIt.exts.awt.fonts.AWTFont;
import com.nulldev.util.graphics.renderIt.renderer.global.rColor;
import com.nulldev.util.graphics.renderIt.renderer.swing.smoothCanvas.RenderMethod;
import com.nulldev.util.graphics.renderIt.renderer.swing.smoothCanvas.SyncMethod;
import com.nulldev.util.graphics.renderIt.util.osdep.OSDependentExtensions;
import com.nulldev.util.graphics.renderIt.util.swing.RITStringUtils;
import com.nulldev.util.gui.swingAPI.Swing;
import com.nulldev.util.io.IOUtils;
import com.nulldev.util.manage.Version;
import com.nulldev.util.media.nAV.renderIt.IVideoSurface;

class NitrousRenderInterface extends renderItGraphicsInterface {
	private final renderItOperations[] SUPPORTED_OPS;

	private static final Version BACKEND_VERSION = new Version(1, 0, 1);
	private static final renderItOperations[] core_ops = new renderItOperations[]
		{ renderItOperations.RIT_EXT_basic_drawing, renderItOperations.RIT_EXT_basic_shapes, renderItOperations.RIT_EXT_viewport_info,
				renderItOperations.RIT_EXT_viewport_offset, renderItOperations.RIT_EXT_versions, renderItOperations.RIT_SWING_panel_control,
				renderItOperations.RIT_EXT_color, renderItOperations.RIT_canvas_clear, renderItOperations.RIT_font_file_formats,
				renderItOperations.NITROUS_accel_info, renderItOperations.RIT_2D_basic_drawing, renderItOperations.RIT_3D_matrix,
				renderItOperations.RIT_2D_matrix, renderItOperations.RIT_2D_vector, renderItOperations.RIT_3D_vector,
				renderItOperations.RIT_3D_psuedo_basic_drawing, renderItOperations.RIT_RIO_closable, renderItOperations.RIT_font_ops,
				renderItOperations.RIT_font_create, renderItOperations.RIT_text_string, renderItOperations.RIT_text_anti_aliasing,
				renderItOperations.RIT_canvas_clear, renderItOperations.RIT_AWT_font_conversion, renderItOperations.RIT_EXT_vsync,
				renderItOperations.RIT_SWING_image_draw, renderItOperations.RIT_media_drawable_vsurface, renderItOperations.RIT_visuals_main,
				renderItOperations.RIT_AWT_font_metrics, renderItOperations.RIT_text_string_scalable, renderItOperations.RIT_EXT_viewport_control,
				renderItOperations.RIT_AWT_text_width, renderItOperations.RIT_SWING_text_width, renderItOperations.RIT_text_string_scalable_offset,
				renderItOperations.RIT_canvas_copy_area, renderItOperations.TOOLKIT_pipeline_flush,

				/* LCDUI */

				renderItOperations.RIT_LCDUI_clip,

				/* GL stuff */

				renderItOperations.GL_compat_vectors, renderItOperations.GL_compat_vertex, renderItOperations.GL_compat_direction, };
	private static final renderItOperations[] paneled_ops = new renderItOperations[]
		{ renderItOperations.RIT_WM_window, renderItOperations.RIT_WM_window_position, renderItOperations.RIT_nullutil_swing_api,
				renderItOperations.RIT_SWING_render_method, renderItOperations.RIT_AWT_render_method };

	private final AtomicReference<Graphics> g;
	private final JPanel jp;
	private final JFrame jfr;
	private final IRenderCanvas canvas;

	public NitrousRenderInterface(final Graphics g, final IRenderCanvas canvas, final JPanel jp) {
		this(g, canvas, jp, null);
	}

	public NitrousRenderInterface(final Graphics g, final IRenderCanvas canvas, final JPanel jp, final JFrame jfr) {
		this.g = new AtomicReference<Graphics>(g);
		this.jp = jp;
		this.canvas = canvas;
		this.jfr = jfr;
		renderItOperations[] tops = core_ops;
		if (jfr != null) {
			tops = ArrayUtils.addAll(tops, OSDependentExtensions.get_os_operations(g));
			tops = ArrayUtils.addAll(tops, paneled_ops);
		}
		if (this.canvas != null && this.canvas.supportsOperation(renderItCanvasOperations.RIT_graphics_deferred))
			tops = ArrayUtils.add(tops, renderItOperations.RIT_EXT_deferred_rendering);
		this.SUPPORTED_OPS = tops;
	}

	@Override
	public boolean supportsOperations(final renderItOperations operation) {
		return ArrayUtils.contains(SUPPORTED_OPS, operation);
	}

	@Override
	public renderItOperations[] supportedOperations() {
		return SUPPORTED_OPS;
	}

	protected void updateGraphics(final Graphics g) {
		this.g.lazySet(g);
	}

	protected void updateGraphicsNow(final Graphics g) {
		this.g.set(g);
	}

	@Override
	public Object rit_get(final int RIT_CONSTANT) {
		switch (RIT_CONSTANT) {
			case RIT_RENDERER_NAME:
				return "Nitrous";
			case RIT_RENDERER_VENDOR:
				return "nullUtil";
			case RIT_RENDERER_VERSION:
				return BACKEND_VERSION.asVersionString();
			case RIT_BACKEND_GET_CONTEXT_BEHAVIOUR:
				return RIT_BACKEND_BEHAVIOUR_SINGLE_CONTEXT;
			case RIT_BACKEND_GET_COMPAT_IMAGE_MODE:
			case RIT_BACKEND_GET_2D_PREFERRED_HINT:
				return RIT_TRUE;
			case RIT_BACKEND_GET_3D_PREFERRED_HINT:
				return RIT_FALSE;
			case RIT_BACKEND_GET_BUFFERING_BEHAVIOUR:
				return RIT_DIRECT_SCANOUT;
			case RIT_BACKEND_GET_RENDER_DIRECTION: {
				switch (this.dir.get()) {
					case GL_DIRECTION_TYPE_INV_Y:
						return RIT_BACKEND_RENDER_DIRECTION_BOTTOM_LEFT;
					case GL_DIRECTION_TYPE_SWING:
					default:
						return RIT_BACKEND_RENDER_DIRECTION_TOP_LEFT;
				}
			}
			case RIT_BACKEND_GET_IDLE_RENDER_BEHAVIOUR:
				switch (((NitrousRenderPane) this.jp).getRenderMethod()) {
					case UPDATE:
						return RIT_BACKEND_BEHAVIOUR_SLOWDOWN;
					default:
						return RIT_BACKEND_BEHAVIOUR_NORMAL;
				}
			case RIT_BACKEND_GET_RENDERER_TYPE:
				switch (((NitrousRenderPane) this.jp).getBackendName()) {
					case "D3DRenderManager":
					case "XRenderManager":
						return (nullutil_swing_isUsingHWAccel() ? RIT_BACKEND_RENDERER_TYPE_HARDWARE : RIT_BACKEND_RENDERER_TYPE_SOFTWARE);
					case "WGLRenderManager":
						return RIT_BACKEND_RENDERER_TYPE_HARDWARE;
					default:
						return RIT_BACKEND_RENDERER_TYPE_SOFTWARE;
				}
			case RIT_GET_SCANOUT_METHOD:
				return RIT_DIRECT_SCANOUT;
			default:
				return RIT_NOT_SUPPORTED;
		}
	}

	@Override
	public Object rit_set(int RIT_CONSTANT, Object RIT_OBJECT) {
		switch (RIT_CONSTANT) {
			default:
				return RIT_NOT_SUPPORTED;
		}
	}

	/* RIT_EXT_basic_drawing */

	@Override
	public void bd_drawRect(final int x, final int y, final int w, final int h) {
		this.bd_drawRect(x, y, w, h, null);
	}

	@Override
	public void bd_drawRect(final int x, final int y, final int w, final int h, final rColor color) {
		if (color != null)
			g.get().setColor(color.toAWTColor());
		g.get().drawRect(x, y, w, h);
	}

	@Override
	public void bd_fillRect(final int x, final int y, final int w, final int h) {
		this.bd_fillRect(x, y, w, h, null);
	}

	@Override
	public void bd_fillRect(final int x, final int y, final int w, final int h, final rColor color) {
		if (color != null)
			g.get().setColor(color.toAWTColor());
		g.get().fillRect(x, y, w, h);
	}

	@Override
	public void bd_putPixel(final int x, final int y) {
		this.bd_putPixel(x, y, null);
	}

	@Override
	public void bd_putPixel(final int x, final int y, final rColor color) {
		if (color != null)
			g.get().setColor(color.toAWTColor());
		g.get().drawRect(x, y, 1, 1);
	}

	@Override
	public void bd_drawLine(final int x1, final int y1, final int x2, final int y2) {
		this.bd_drawLine(x1, y1, x2, y2, null);
	}

	@Override
	public void bd_drawLine(final int x1, final int y1, final int x2, final int y2, final rColor color) {
		if (color != null)
			g.get().setColor(color.toAWTColor());
		g.get().drawLine(x1, y1, x2, y2);
	}

	/* RIT_EXT_basic_shapes */

	public void bd_drawOval(final int x, final int y, final int width, final int height) {
		this.bd_drawOval(x, y, width, height, null);
	}

	public void bd_drawOval(final int x, final int y, final int width, final int height, final rColor color) {
		if (color != null)
			g.get().setColor(color.toAWTColor());
		this.g.get().drawOval(x, y, width, height);
	}

	public void bd_drawCircle(final int x, final int y, final int r) {
		this.bd_drawCircle(x, y, r, null);
	}

	public void bd_drawCircle(final int x, final int y, final int r, final rColor color) {
		if (color != null)
			g.get().setColor(color.toAWTColor());
		this.g.get().drawOval(x, y, r, r);
	}

	public void bd_fillOval(final int x, final int y, final int width, final int height) {
		this.bd_fillOval(x, y, width, height, null);
	}

	public void bd_fillOval(final int x, final int y, final int width, final int height, final rColor color) {
		if (color != null)
			g.get().setColor(color.toAWTColor());
		this.g.get().fillOval(x, y, width, height);
	}

	public void bd_fillCircle(final int x, final int y, final int r) {
		this.bd_fillCircle(x, y, r, null);
	}

	public void bd_fillCircle(final int x, final int y, final int r, final rColor color) {
		if (color != null)
			g.get().setColor(color.toAWTColor());
		this.g.get().fillOval(x, y, r, r);
	}

	/* RIT_EXT_viewport_info */

	@Override
	public int viewport_getWidth() {
		return jp.getWidth();
	}

	@Override
	public int viewport_getHeight() {
		return jp.getHeight();
	}

	/* RIT_EXT_viewport_offset */

	@Override
	public int viewport_getXOffset() {
		return 0;
	}

	@Override
	public int viewport_getYOffset() {
		return 0;
	}

	/* RIT_WM_window_position */

	@Override
	public void wm_setWindowPosX(final int x) {
		this.checkValidInt(RIT_VALUE_TYPE_X, x);
		if (this.jfr != null) {
			this.jfr.setLocation(x, (int) this.jfr.getLocation().getY());
		} else
			this.unsupported();
	}

	@Override
	public void wm_setWindowPosY(final int y) {
		this.checkValidInt(RIT_VALUE_TYPE_Y, y);
		if (this.jfr != null) {
			this.jfr.setLocation((int) this.jfr.getLocation().getX(), y);
		} else
			this.unsupported();
	}

	@Override
	public int wm_getPosX() {
		if (this.jfr != null) {
			return (int) this.jfr.getLocation().getX();
		} else
			return RIT_NOT_SUPPORTED;
	}

	@Override
	public int wm_getPosY() {
		if (this.jfr != null) {
			return (int) this.jfr.getLocation().getY();
		} else
			return RIT_NOT_SUPPORTED;
	}

	/* RIT_EXT_versions */

	@Override
	public String backend_version() {
		return BACKEND_VERSION.asVersionString();
	}

	/* RIT_SWING_panel_control */

	@Override
	public void swing_panel_doubleBuffer(final boolean doubleBuffer) {
		this.jp.setDoubleBuffered(doubleBuffer);
	}

	@Override
	public boolean swing_panel_isDoubleBuffered() {
		return this.jp.isDoubleBuffered();
	}

	@Override
	public void swing_panel_setSize(final int width, final int height) {
		this.checkValidInt(RIT_VALUE_TYPE_WIDTH, width);
		this.checkValidInt(RIT_VALUE_TYPE_HEIGHT, height);
		this.jp.setSize(width, height);
	}

	@Override
	public int swing_panel_getSizeX() {
		return (int) this.jp.getSize().getWidth();
	}

	@Override
	public int swing_panel_getSizeY() {
		return (int) this.jp.getSize().getHeight();
	}

	/* RIT_EXT_color */

	@Override
	public rColor getColor() {
		return rColor.as(this.g.get().getColor());
	}

	@Override
	public void setColor(final rColor color) {
		g.get().setColor(color.toAWTColor());
	}

	@Override
	public void setColor(final int color) {
		this.checkValidInt(RIT_VALUE_TYPE_COLOR_RGB, color);
		g.get().setColor(new Color(color));
	}

	@Override
	public void setColor(final int r, final int g, final int b) {
		this.checkValidInt(RIT_VALUE_TYPE_COLOR_R, r);
		this.checkValidInt(RIT_VALUE_TYPE_COLOR_G, g);
		this.checkValidInt(RIT_VALUE_TYPE_COLOR_B, b);
		this.g.get().setColor(new Color(r, g, b));
	}

	@Override
	public void setColor(final double r, final double g, final double b, final double a) {
		this.checkValidDouble(RIT_VALUE_TYPE_COLOR_R, r);
		this.checkValidDouble(RIT_VALUE_TYPE_COLOR_G, g);
		this.checkValidDouble(RIT_VALUE_TYPE_COLOR_B, b);
		this.checkValidDouble(RIT_VALUE_TYPE_COLOR_A, a);
		this.g.get().setColor(new Color((float) r, (float) g, (float) b, (float) a));
	}

	/* RIT_nullutil_swing_api */

	@Override
	public void nullutil_swing_center() {
		if (this.jfr != null)
			Swing.center(this.jfr);
		else
			this.unsupported(renderItOperations.RIT_nullutil_swing_api);
	}

	@Override
	public int nullutil_swing_getRefreshRate() {
		return (int) Swing.refreshRate();
	}

	@Override
	public int nullutil_swing_stringWidth(final Object fontMetrics, final String line) {
		this.checkValidObject(RIT_VALUE_TYPE_AWT_FONT_METRICS, fontMetrics);
		this.checkValidObject(RIT_VALUE_TYPE_STRING, line);
		return Swing.stringWidth((FontMetrics) fontMetrics, line);
	}

	@Override
	public boolean nullutil_swing_isUsingHWAccel() {
		return Swing.isUsingHWAccel();
	}

	/* RIT_WM_window */

	@Override
	public String wm_getTitle() {
		if (this.jfr != null)
			return this.jfr.getTitle();
		else
			return null;
	}

	@Override
	public void wm_setTitle(final String title) {
		this.checkValidObject(RIT_VALUE_TYPE_STRING, title);
		if (this.jfr != null)
			this.jfr.setTitle(title);
		else
			this.unsupported(renderItOperations.RIT_WM_window);
	}

	@Override
	public void wm_setWidth(final int width) {
		this.checkValidInt(RIT_VALUE_TYPE_WIDTH, width);
		if (this.jfr != null)
			this.jfr.setSize(width, this.jfr.getHeight());
		else
			this.unsupported(renderItOperations.RIT_WM_window);
	}

	@Override
	public void wm_setHeight(final int height) {
		this.checkValidInt(RIT_VALUE_TYPE_HEIGHT, height);
		if (this.jfr != null)
			this.jfr.setSize(this.jfr.getWidth(), height);
		else
			this.unsupported(renderItOperations.RIT_WM_window);
	}

	@Override
	public int wm_getWidth() {
		if (this.jfr != null)
			return this.jfr.getWidth();
		else
			return RIT_NOT_SUPPORTED;
	}

	@Override
	public int wm_getHeight() {
		if (this.jfr != null)
			return this.jfr.getHeight();
		else
			return RIT_NOT_SUPPORTED;
	}

	@Override
	public boolean wm_isFullScreen() {
		return false;
	}

	/* RIT_canvas_clear */

	@Override
	public void canvas_clear(final int x, final int y, final int width, final int height) {
		this.canvas_clear(x, y, width, height, null);
	}

	@Override
	public void canvas_clear(final int x, final int y, final int width, final int height, final rColor color) {
		if (color == null)
			this.g.get().clearRect(x, y, width, height);
		else {
			RITViewportUtils.clearCanvas(this, x, y, width, height, color);
		}
	}

	/* RIT_SWING_render_method */

	@Override
	public int swing_getRenderMethod() {
		if (this.jp != null && this.jp instanceof NitrousRenderPane)
			switch (((NitrousRenderPane) this.jp).getRenderMethod()) {
				case PAINT:
					return RIT_SWING_PANEL_PAINT;
				case PAINT_ALL:
					return RIT_SWING_PANEL_PAINT_ALL;
				case REPAINT:
					return RIT_SWING_PANEL_REPAINT;
				case REPAINT_WITHIN_SYNC:
					return RIT_SWING_PANEL_REPAINT_WITHIN_SYNC;
				case UPDATE:
					return RIT_SWING_PANEL_UPDATE;
				default:
					return RIT_NOT_SUPPORTED;
			}
		return RIT_NOT_SUPPORTED;
	}

	@Override
	public void swing_setRenderMethod(final int renderMethod) {
		this.checkValidInt(RIT_VALUE_TYPE_RENDER_METHOD_INT, renderMethod);
		((NitrousRenderPane) this.jp).setRenderMethod(RenderMethod.fromRIT(renderMethod));
	}

	@Override
	public boolean swing_supportsRenderMethod(final int renderMethod) {
		switch (renderMethod) {
			case RIT_SWING_PANEL_PAINT:
			case RIT_SWING_PANEL_PAINT_ALL:
			case RIT_SWING_PANEL_REPAINT:
			case RIT_SWING_PANEL_REPAINT_WITHIN_SYNC:
			case RIT_SWING_PANEL_UPDATE:
				return true;
			default:
				return false;
		}
	}

	/* RIT_AWT_render_method */

	@Override
	public int awt_getRenderMethod() {
		return this.swing_getRenderMethod();
	}

	public void awt_setRenderMethod(final int renderMethod) {
		this.swing_setRenderMethod(renderMethod);
	}

	public boolean awt_supportsRenderMethod(final int renderMethod) {
		return this.swing_supportsRenderMethod(renderMethod);
	}

	/* RIT_font_file_formats */

	@Override
	public boolean supportsFontFileFormat(final int fontFormat) {
		this.checkValidInt(RIT_VALUE_TYPE_FONT_FORMAT, fontFormat);
		switch (fontFormat) {
			case RIT_FONT_FORMAT_OTF:
			case RIT_FONT_FORMAT_TTF:
				return true;
			default:
				return false;
		}
	}

	/* NITROUS_accel_info */

	@Override
	public String nitrous_getBackend() {
		return ((NitrousRenderPane) this.jp).getBackendName();
	}

	/* RIT_2D_basic_drawing */

	@Override
	public void rit_2d_drawPoint(final Vector2f vec) {
		this.checkValidObject(RIT_VALUE_TYPE_VEC_2F, vec);
		final int posX = (int) (viewport_getWidth() * vec.getX());
		final int posY = (int) (viewport_getWidth() * vec.getY());
		this.g.get().drawRect(posX, posY, 1, 1);
	}

	@Override
	public void rit_2d_drawLine(final Vector2f start, final Vector2f end) {
		this.checkValidObject(RIT_VALUE_TYPE_VEC_2F, start);
		this.checkValidObject(RIT_VALUE_TYPE_VEC_2F, end);
		final int startX = (int) (viewport_getWidth() * start.getX());
		final int startY = (int) (viewport_getWidth() * start.getY());
		final int endX = (int) (viewport_getWidth() * end.getX());
		final int endY = (int) (viewport_getWidth() * end.getY());
		this.g.get().drawLine(startX, startY, endX, endY);
	}

	@Override
	public void rit_2d_drawRect(final Vector2f pos, final int width, final int height) {
		this.checkValidObject(RIT_VALUE_TYPE_VEC_2F, pos);
		final int startX = (int) (viewport_getWidth() * pos.getX());
		final int startY = (int) (viewport_getWidth() * pos.getY());
		this.g.get().drawRect(startX, startY, width, height);
	}

	@Override
	public void rit_2d_drawRect(final Vector2f pos, final Vector2f dim) {
		this.checkValidObject(RIT_VALUE_TYPE_VEC_2F, pos);
		this.checkValidObject(RIT_VALUE_TYPE_VEC_2F, dim);
		final int startX = (int) (viewport_getWidth() * pos.getX());
		final int startY = (int) (viewport_getWidth() * pos.getY());
		final int endX = (int) (viewport_getWidth() * dim.getX());
		final int endY = (int) (viewport_getWidth() * dim.getY());
		this.g.get().drawRect(startX, startY, endX, endY);
	}

	@Override
	public void rit_2d_fillRect(final Vector2f pos, final int width, final int height) {
		this.checkValidObject(RIT_VALUE_TYPE_VEC_2F, pos);
		final int startX = (int) (viewport_getWidth() * pos.getX());
		final int startY = (int) (viewport_getWidth() * pos.getY());
		this.g.get().fillRect(startX, startY, width, height);
	}

	@Override
	public void rit_2d_fillRect(final Vector2f pos, final Vector2f dim) {
		this.checkValidObject(RIT_VALUE_TYPE_VEC_2F, pos);
		this.checkValidObject(RIT_VALUE_TYPE_VEC_2F, dim);
		final int startX = (int) (viewport_getWidth() * pos.getX());
		final int startY = (int) (viewport_getWidth() * pos.getY());
		final int endX = (int) (viewport_getWidth() * dim.getX());
		final int endY = (int) (viewport_getWidth() * dim.getY());
		this.g.get().fillRect(startX, startY, endX, endY);
	}

	/* RIT_3D_psuedo_basic_drawing */

	@Override
	public void rit_3d_draw_hRect(final Vector4f rect, final boolean raise) {
		this.checkValidObject(RIT_VALUE_TYPE_VEC_4F, rect);
		final int startX = (int) (viewport_getWidth() * rect.getX());
		final int startY = (int) (viewport_getWidth() * rect.getY());
		final int endX = (int) (viewport_getWidth() * rect.getZ());
		final int endY = (int) (viewport_getWidth() * rect.getW());
		this.g.get().draw3DRect(startX, startY, endX, endY, raise);
	}

	@Override
	public void rit_3d_draw_hRect(final Vector2f pos, final int width, final int height, final boolean raise) {
		this.checkValidObject(RIT_VALUE_TYPE_VEC_2F, pos);
		final int startX = (int) (viewport_getWidth() * pos.getX());
		final int startY = (int) (viewport_getWidth() * pos.getY());
		this.g.get().draw3DRect(startX, startY, width, height, raise);
	}

	@Override
	public void rit_3d_fill_hRect(final Vector4f rect, final boolean raise) {
		this.checkValidObject(RIT_VALUE_TYPE_VEC_4F, rect);
		final int startX = (int) (viewport_getWidth() * rect.getX());
		final int startY = (int) (viewport_getWidth() * rect.getY());
		final int endX = (int) (viewport_getWidth() * rect.getZ());
		final int endY = (int) (viewport_getWidth() * rect.getW());
		this.g.get().fill3DRect(startX, startY, endX, endY, raise);
	}

	@Override
	public void rit_3d_fill_hRect(final Vector2f pos, final int width, final int height, final boolean raise) {
		this.checkValidObject(RIT_VALUE_TYPE_VEC_2F, pos);
		final int startX = (int) (viewport_getWidth() * pos.getX());
		final int startY = (int) (viewport_getWidth() * pos.getY());
		this.g.get().fill3DRect(startX, startY, width, height, raise);
	}

	/* GL_compat_vertex */

	@Override
	public void glVertex2f(final float x, final float y) {
		switch (this.ai.get()) {
			case GL_VECTOR_TYPE_COORD_FLOAT:
				this.rit_2d_drawPoint(new Vector2f(x, y));
				break;
			case GL_VECTOR_TYPE_COORD_INT:
				switch (this.dir.get()) {
					case GL_DIRECTION_TYPE_INV_Y: {
						this.bd_putPixel((int) x, (viewport_getHeight() - (int) y));
						break;
					}
					case GL_DIRECTION_TYPE_INV_X: {
						this.bd_putPixel((viewport_getWidth() - (int) x), (int) y);
						break;
					}
					case GL_DIRECTION_TYPE_INV_XY: {
						this.bd_putPixel((viewport_getWidth() - (int) x), (viewport_getHeight() - (int) y));
						break;
					}
					case GL_DIRECTION_TYPE_SWING:
					default: {
						this.rit_2d_drawPoint(new Vector2f((x / viewport_getWidth()), (y / viewport_getHeight())));
						break;
					}

				}
				break;
			default:
				throw new UnsupportedOperationException("Unknown type: " + this.ai.get());
		}
	}

	/* GL_compat_vectors */

	private final AtomicInteger ai = new AtomicInteger(GL_VECTOR_TYPE_COORD_FLOAT);

	@Override
	public void glTreatVectorsAs(final int type) {
		this.checkValidInt(RIT_VALUE_TYPE_GL_VECTOR_TYPE, type);
		this.ai.set(type);
	}

	@Override
	public int glGetVectorTreatment() {
		return this.ai.get();
	}

	/* GL_compat_direction */

	private final AtomicInteger dir = new AtomicInteger(GL_DIRECTION_TYPE_SWING);

	public int glCompatGetDirection() {
		return dir.get();
	}

	public void glCompatSetDirection(final int type) {
		this.checkValidInt(RIT_VALUE_TYPE_GL_DIRECTION_TYPE, type);
		this.dir.set(type);
	}

	/* RIT_RIO_closeable */

	@Override
	public void closeOperations() {
		if (this.g.get() != null) {
			this.g.get().dispose();
			this.g.lazySet(null);
		}
	}

	/* RIT_font_ops */

	@Override
	public void font_destroy(final RITFont font) {
		IOUtils.closeQuietly(font);
	}

	@Override
	public int font_get_format(final RITFont font) {
		return font.getFormat();
	}

	@Override
	public RITFont font_clone(final RITFont font, final int size, final int style) {
		return font.deriveToSize(size).deriveToStyle(style);
	}

	@Override
	public RITFont font_clone(final RITFont font, final float size, final int style) {
		return font.deriveToSize(size).deriveToStyle(style);
	}

	@Override
	public RITFont font_clone(final RITFont font, final int size) {
		return font.deriveToSize(size);
	}

	@Override
	public RITFont font_clone(final RITFont font, final float size) {
		return font.deriveToSize(size);
	}

	/* RIT_font_create */

	@Override
	public RITFont font_create(final String name, final int size, final int style) {
		return AWTFont.create(name, size, style);
	}

	@Override
	public RITFont font_create(final String name, final float size, final int style) {
		return AWTFont.create(name, size, style);
	}

	@Override
	public RITFont font_create(final String name, final int size) {
		return AWTFont.create(name, size);
	}

	@Override
	public RITFont font_create(final String name, final float size) {
		return AWTFont.create(name, size);
	}

	@Override
	public RITFont font_create(final File file) {
		return AWTFont.create(file);
	}

	/* RIT_text_string */

	@Override
	public void text_string(final String text, final int x, final int y, final RITFont font) {
		this.text_string(text, x, y, font, null);
	}

	@Override
	public void text_string(final String text, final int x, final int y, final RITFont font, final rColor color) {
		if (font != null) {
			if (font instanceof AWTFont) {
				this.g.get().setFont((Font) font.getFontObject());
			} else
				this.unsupported(renderItOperations.RIT_text_string);
		}
		if (color != null)
			this.g.get().setColor(color.toAWTColor());
		this.g.get().drawString(text, x, y);
	}

	@Override
	public void text_shadowedString(final String text, final int x, final int y, final RITFont font) {
		this.text_shadowedString(text, x, y, font, null);
	}

	@Override
	public void text_shadowedString(final String text, final int x, final int y, final RITFont font, final rColor c) {
		this.text_string(text, x - 1, y + 1, font, rColor.black);
		this.text_string(text, x, y, font, c != null ? c : rColor.white);
	}

	/* RIT_text_anti_aliasing */

	@Override
	public boolean text_isAAPresent() {
		if (this.jp instanceof NitrousRenderPane)
			return ((NitrousRenderPane) this.jp).isAA();
		else
			return false;
	}

	@Override
	public void text_setAAPresence(final boolean value) {
		if (this.jp instanceof NitrousRenderPane)
			((NitrousRenderPane) this.jp).setAA(value);
		else
			this.unsupported(renderItOperations.RIT_text_anti_aliasing);
	}

	/* RIT_AWT_font_conversion */

	@Override
	public RITFont awt_make_font(final Object font) {
		if (font instanceof java.awt.Font) {
			return AWTFont.from((java.awt.Font) font);
		} else if (font instanceof com.nulldev.util.gui.fontAPI.Font) {
			return AWTFont.from(((com.nulldev.util.gui.fontAPI.Font) font).getFont());
		} else
			return this.ret_unsupported(renderItOperations.RIT_AWT_font_conversion);
	}

	/* RIT_AWT_current_font */

	@Override
	public Object awt_getCurrentFont() {
		if (this.g.get() == null)
			return null;
		else
			return this.g.get().getFont();
	}

	/* RIT_AWT_font_metrics */

	@Override
	public Object awt_getFontMetrics(final RITFont font) {
		if (this.g.get() == null)
			return null;
		if (!(font instanceof AWTFont))
			return null;
		final AWTFont awf = (AWTFont) font;
		return this.g.get().getFontMetrics((java.awt.Font) awf.getFontObject());
	}

	/* RIT_AWT_has_renderable */

	@Override
	public boolean awt_hasRenderable() {
		return this.g.get() != null;
	}

	/* RIT_EXT_vsync */

	public void vsync_syncToDisplay(final int display) {
		switch (display) {
			case 2:
			case 1:
			case 0: {
				RITVSyncTimer.get().waitForBlank();
				break;
			}
			case -1:
			default: {
				Toolkit.getDefaultToolkit().sync();
				break;
			}
		}
	}

	public boolean vsync_isEnabled() {
		if (this.jp instanceof NitrousRenderPane)
			return ((NitrousRenderPane) this.jp).getSyncMethod() != SyncMethod.NO_SYNC;
		else
			return true;
	}

	public boolean vsync_supportsAdaptiveSync() {
		return false;
	}

	public void vsync_set(final int value) {
		if (this.jp instanceof NitrousRenderPane) {
			switch (value) {
				case RIT_VSYNC_DISABLED: {
					((NitrousRenderPane) this.jp).setSyncMethod(SyncMethod.NO_SYNC);
					break;
				}
				case RIT_VSYNC_DOUBLE_BUFFERED:
				case RIT_VSYNC_TRIPLE_BUFFERED:
				case RIT_VSYNC_ADAPTIVE:
				case RIT_VSYNC_ENABLED: {
					((NitrousRenderPane) this.jp).setSyncMethod(SyncMethod.VERTICAL_SYNC);
					break;
				}
				default:
					break;
			}
		} else
			this.unsupported(renderItOperations.RIT_EXT_vsync);
	}

	/* RIT_SWING_image_draw */

	@Override
	public void swing_drawImage(final Object image, final int x, final int y) {
		this.checkValidInt(RIT_VALUE_TYPE_X, x);
		this.checkValidInt(RIT_VALUE_TYPE_Y, y);
		if (!(image instanceof java.awt.Image))
			throw new UnsupportedOperationException("Unsupported graphics operation! (Unknown image: " + image + ")");
		this.g.get().drawImage(((java.awt.Image) image), x, y, null);
	}

	@Override
	public void swing_drawImage(final Object image, final int x, final int y, final int width, final int height) {
		this.checkValidInt(RIT_VALUE_TYPE_X, x);
		this.checkValidInt(RIT_VALUE_TYPE_Y, y);
		if (!(image instanceof java.awt.Image))
			throw new UnsupportedOperationException("Unsupported graphics operation! (Unknown image: " + image + ")");
		this.g.get().drawImage(((java.awt.Image) image), x, y, width, height, null);
	}

	@Override
	public void swing_drawImage(final Object image, final int x, final int y, final int width, final int height, final Object observer) {
		this.checkValidInt(RIT_VALUE_TYPE_X, x);
		this.checkValidInt(RIT_VALUE_TYPE_Y, y);
		if (!(image instanceof java.awt.Image))
			throw new UnsupportedOperationException("Unsupported graphics operation! (Unknown image: " + image + ")");
		if (!(observer instanceof java.awt.image.ImageObserver))
			throw new UnsupportedOperationException("Unsupported graphics operation! (Unknown observer: " + observer + ")");
		this.g.get().drawImage(((java.awt.Image) image), x, y, width, height, (java.awt.image.ImageObserver) observer);
	}

	/* RIT_media_drawable_vsurface */

	@Override
	public void media_importSurface(final Object obj, final int x, final int y, final int width, final int height) {
		this.checkValidInt(RIT_VALUE_TYPE_X, x);
		this.checkValidInt(RIT_VALUE_TYPE_Y, y);
		if (!(obj instanceof IVideoSurface))
			throw new UnsupportedOperationException("Unsupported graphics operation! (Unknown surface: " + obj + ")");
		((IVideoSurface) obj).renderTo(this, x, y, width, height);
	}

	/* RIT_text_string_scalable */

	@Override
	public int text_scalable(final String text, final int x, final int y, final RITFont font) {
		return this.text_scalable(text, x, y, font, null);
	}

	@Override
	public int text_scalable(final String text, final int x, final int y, final RITFont font, final rColor color) {
		final variable<Integer> _y = new variable<Integer>(y);
		final int inc_am = MathUtil.clamp(2, 96, (int) font.getFontSize());
		final int max_width = (int) (viewport_getWidth() - (2 * font.getFontSize()));
		RITStringUtils.wrap(text, this.g.get().getFontMetrics((Font) font.getFontObject()), max_width).forEach(line -> {
			NitrousRenderInterface.this.text_string(line, x, _y.get(), font, color);
			_y.set(_y.get() + inc_am);
		});
		return (_y.get() - y);
	}

	/* RIT_EXT_viewport_control */

	@Override
	public boolean viewport_requestSize(final int width, final int height) {
		if (height < 0 || width < 0)
			return false;
		try {
			this.jp.setPreferredSize(new Dimension(width, height));
			return true;
		} catch (Throwable tx) {
			return false;
		}
	}

	@Override
	public boolean viewport_requestHeight(final int height) {
		if (height < 0)
			return false;
		try {
			this.jp.setPreferredSize(new Dimension(this.jp.getWidth(), height));
			return true;
		} catch (Throwable tx) {
			return false;
		}
	}

	@Override
	public boolean viewport_requestWidth(final int width) {
		if (width < 0)
			return false;
		try {
			this.jp.setPreferredSize(new Dimension(width, this.jp.getHeight()));
			return true;
		} catch (Throwable tx) {
			return false;
		}
	}

	/* RIT_text_string_scalable_offset */

	@Override
	public int text_scalable(final String text, final int x, final int y, final int offset, final RITFont font) {
		return this.text_scalable(text, x, y, offset, font, null);
	}

	@Override
	public int text_scalable(final String text, final int x, final int y, final int offset, final RITFont font, final rColor color) {
		final variable<Integer> _y = new variable<Integer>(y);
		final int inc_am = MathUtil.clamp(2, 96, (int) font.getFontSize());
		final int max_width = (int) (viewport_getWidth() - (2 * font.getFontSize()) - offset);
		RITStringUtils.wrap(text, this.g.get().getFontMetrics((Font) font.getFontObject()), max_width).forEach(line -> {
			NitrousRenderInterface.this.text_string(line, x, _y.get(), font, color);
			_y.set(_y.get() + inc_am);
		});
		return (_y.get() - y);
	}

	/* RIT_EXT_deferred_rendering */

	@Override
	public boolean rit_dr_isEnabled() {
		if (!this.canvas.supportsOperation(renderItCanvasOperations.RIT_graphics_deferred))
			return false;
		return this.canvas.graphics_isDeferred();
	}

	@Override
	public void rit_dr_setEnabled(final boolean state) {
		if (!this.canvas.supportsOperation(renderItCanvasOperations.RIT_graphics_deferred))
			this.unsupported(renderItOperations.RIT_EXT_deferred_rendering);
		this.canvas.graphics_setDeferred(state);
	}

	@Override
	public void rit_dr_requestPaint() {
		if (!this.canvas.supportsOperation(renderItCanvasOperations.RIT_graphics_deferred))
			this.unsupported(renderItOperations.RIT_EXT_deferred_rendering);
		this.canvas.graphics_requestRepaint();
	}

	/* RIT_AWT_text_width */

	@Override
	public int awt_textWidth(final String text) {
		if (this.g.get() == null)
			return 0;
		if (text.length() <= 0 || StringsUtil.isBlank(text))
			return 0;
		return this.g.get().getFontMetrics().stringWidth(text);
	}

	/* RIT_SWING_text_width */

	@Override
	public int swing_textWidth(final String text) {
		if (this.g.get() == null)
			return 0;
		if (text.length() <= 0 || StringsUtil.isBlank(text))
			return 0;
		return this.g.get().getFontMetrics().stringWidth(text);
	}

	/* RIT_LCDUI_clip */

	@Override
	public int lcdui_clip_getHeight() {
		if (this.g.get() == null)
			return RIT_NOT_SUPPORTED;
		final Rectangle rect = this.g.get().getClipBounds();
		if (rect == null)
			return RIT_NOT_SUPPORTED;
		return rect.height;
	}

	@Override
	public int lcdui_clip_getWidth() {
		if (this.g.get() == null)
			return RIT_NOT_SUPPORTED;
		final Rectangle rect = this.g.get().getClipBounds();
		if (rect == null)
			return RIT_NOT_SUPPORTED;
		return rect.width;
	}

	@Override
	public int lcdui_clip_getX() {
		if (this.g.get() == null)
			return RIT_NOT_SUPPORTED;
		final Rectangle rect = this.g.get().getClipBounds();
		if (rect == null)
			return RIT_NOT_SUPPORTED;
		return rect.x;
	}

	@Override
	public int lcdui_clip_getY() {
		if (this.g.get() == null)
			return RIT_NOT_SUPPORTED;
		final Rectangle rect = this.g.get().getClipBounds();
		if (rect == null)
			return RIT_NOT_SUPPORTED;
		return rect.y;
	}

	@Override
	public void lcdui_clip_set(final int x, final int y, final int width, final int height) {
		if (this.g.get() == null)
			return;
		this.checkValidInt(RIT_VALUE_TYPE_X, x);
		this.checkValidInt(RIT_VALUE_TYPE_Y, y);
		this.checkValidInt(RIT_VALUE_TYPE_X, width);
		this.checkValidInt(RIT_VALUE_TYPE_Y, height);
		this.g.get().setClip(x, y, width, height);
	}

	@Override
	public void lcdui_clip_rect(final int x, final int y, final int width, final int height) {
		if (this.g.get() == null)
			return;
		this.checkValidInt(RIT_VALUE_TYPE_X, x);
		this.checkValidInt(RIT_VALUE_TYPE_Y, y);
		this.checkValidInt(RIT_VALUE_TYPE_X, width);
		this.checkValidInt(RIT_VALUE_TYPE_Y, height);
		this.g.get().clipRect(x, y, width, height);
	}

	/* RIT_canvas_copy_area */

	@Override
	public void canvas_copyArea(final int x, final int y, final int width, final int height, final int destX, final int destY) {
		if (this.g.get() == null)
			return;
		this.checkValidInt(RIT_VALUE_TYPE_X, x);
		this.checkValidInt(RIT_VALUE_TYPE_Y, y);
		this.checkValidInt(RIT_VALUE_TYPE_X, destX);
		this.checkValidInt(RIT_VALUE_TYPE_Y, destY);
		this.checkValidInt(RIT_VALUE_TYPE_X, width);
		this.checkValidInt(RIT_VALUE_TYPE_Y, height);
		this.g.get().copyArea(x, y, width, height, destX, destY);
	}

	/* WINDOWS_invalidate_surface */

	@Override
	public void windows_invalidateSurface() {
		OSDependentExtensions.invalidateSurface(this.g.get());
	}

	/* TOOLKIT_pipeline_flush */

	@Override
	public void toolkit_flushPipeline() {
		Toolkit.getDefaultToolkit().sync();
	}
}
